<?php
/***********************************************************
 * 在线升级逻辑定义
 * @作者 pcfcms <1131680521@qq.com>
 * @版权 广州市春风科技有限公司
 * @主页 http://www.pcfcms.com
 * @时间 2019年12月21日
***********************************************************/
namespace app\admin\logic;
use think\facade\Db;
use think\facade\Request;
use think\facade\Cache;

class UpdateLogic
{
    public $root_path;
    public $curent_version; 
	public $version_txt_path;
    public $service_url;
    public $update_url;
    public $service_pcfcms;
	
    function  __construct() 
	{
        $this->service_pcfcms = getupdateData('update');
        $this->root_path = ROOT_PATH; // 当前项目路径
		$this->version_txt_path = $this->root_path.'extend/conf/version.txt'; // 版本文件路径
        $this->curent_version = getCmsVersion(); // 版本号
        $this->service_url = base64_decode($this->service_pcfcms['server'])."update.php"; // 升级地址
        $this->update_url = $this->service_url . '?domain='.Request::rootDomain().'&version='.$this->curent_version.'&onlyid='.$this->service_pcfcms['onlyid'];
    }
	
	// 检查是否有更新包
	public function pcfcms_service($type=0, $urlext='')
	{
		$uconfig = getupdateData('update');
		if(!$uconfig['status'] || $uconfig['status'] == 0){
			return ['code' => 0, 'msg' => '在线升级功能未启用'];
		}
		if(!$uconfig['server']){
			return ['code' => 0, 'msg' => '未配置升级服务器'];
		}
		if(!$uconfig['onlyid']){
			return ['code' => 0, 'msg' => '未配置授权码'];
		}
		$url = base64_decode($uconfig['server']);
		if(substr($url,-1) != '/'){
			$url .= '/';// 强制添加斜杠后续
		}
		$info = getCmsVersion();
		$url .= 'update.php?version='.$info;
		if($urlext){
			$url.="&".$urlext; // 其它附加参数
		}
		if($type == 1 || $type == 4){
			$onlyid = $uconfig['onlyid'] ? $uconfig['onlyid'] : "";//授权码
			$domain = Request::rootDomain(); //授权域名
			$client_ip = serverIP(); //服务器IP
			$url .= "&domain=".rawurlencode($domain)."&ip=".rawurlencode($client_ip);
			$url .= "&onlyid=".$onlyid;
		}
		$json = file_get_contents($url);
		$info = json_decode($json,true);
        if(!empty($info)){
            $upgradeArr = array();
            $introStr = '';
            $upgradeStr = '';
            foreach ($info as $key => $val) {
                $upgrade = !empty($val['upgrade']) ? $val['upgrade'] : array();
                $upgradeArr = array_merge($upgradeArr, $upgrade);
                $introStr .= '<br>'.filter_line_return($val['intro'], '<br>');
            }
            $upgradeArr = array_unique($upgradeArr);
            $upgradeStr = implode('<br>', $upgradeArr); // 升级提示需要覆盖哪些文件
            $introArr = explode('<br>', $introStr);
            $introStr = '更新日志：';
            foreach ($introArr as $key => $val) {
                if (empty($val)) {
                    continue;
                }
                $introStr .= "<br>{$key}、".$val;
            }
            $lastupgrade = $info[count($info) - 1];
            if (!empty($lastupgrade['upgrade_title'])) {
                $introStr .= '<br>'.$lastupgrade['upgrade_title'];
            }
            $lastupgrade['intro'] = htmlspecialchars_decode($introStr);
            $lastupgrade['upgrade'] = htmlspecialchars_decode($upgradeStr); // 升级提示需要覆盖哪些文
            // 升级公告
            if (!empty($lastupgrade['notice'])) {
                $lastupgrade['notice'] = htmlspecialchars_decode($lastupgrade['notice']) . '<br>';
            }
            return ['code' => 2, 'msg' => $lastupgrade];
        }
        return ['code' => 0, 'msg' => '已是最新版'];
	}
	
    // 一键更新
    public  function OneKeyUpgrade()
    {
        error_reporting(0);//关闭所有错误报告
        $allow_url_fopen = ini_get('allow_url_fopen');
        if (!$allow_url_fopen) {
            return ['code' => 0, 'msg' => "请联系空间商，设置 php.ini 中参数 allow_url_fopen = 1"];
        }     
        if (!extension_loaded('zip')) {
            return ['code' => 0, 'msg' => "请联系空间商，开启 php.ini 中的php-zip扩展"];
        }
        $serviceVersionList = file_get_contents($this->update_url);
        $serviceVersionList = json_decode($serviceVersionList,true);
        if (empty($serviceVersionList)) {
            return ['code' => 0, 'msg' => "没找到升级信息"];
        } else if (!empty($serviceVersionList['is_allow_upgrade'] < 0)) {
            return ['code' => 4, 'msg' => $serviceVersionList['is_upgrade_tips']];
        }
        clearstatcache();// 清除文件夹权限缓存
        if (!is_writeable($this->version_txt_path)) {
            return ['code' => 0, 'msg' => '文件'.$this->version_txt_path.' 不可写，不能升级!!!'];
        }
        // 最新更新版本信息
        $lastServiceVersion = $serviceVersionList[count($serviceVersionList) - 1];
        // 批量下载更新包
        $upgradeArr = array(); // 更新的文件列表
        $folderName = $lastServiceVersion['key_num'];
        foreach ($serviceVersionList as $key => $val) {
            // 下载更新包
            $result = $this->downloadFile($val['down_url'], $val['file_md5']);
            if (!isset($result['code']) || $result['code'] != 1) {
                return $result;
            }
            // 第一个循环执行的业务
            if ($key == 0) {
                // 解压到最后一个更新包的文件夹
                $lastDownFileName = explode('/', $lastServiceVersion['down_url']);    
                $lastDownFileName = end($lastDownFileName);
                $folderName = str_replace(".zip", "", $lastDownFileName);// 文件夹
            }
            $downFileName = explode('/', $val['down_url']);    
            $downFileName = end($downFileName);
            // 解压文件
            $zip = new \ZipArchive(); //新建一个ZipArchive的对象
            if ($zip->open($this->root_path.$downFileName) != true) {
                return ['code' => 0, 'msg' => "升级包读取失败!"];
            }
            $zip->extractTo($this->root_path.'/');//假设解压缩到在当前路径下backup文件夹内
            $zip->close();//关闭处理的zip文件
            // 更新的文件列表
            $upgrade = !empty($val['upgrade']) ? $val['upgrade'] : array();
            $upgradeArr = array_merge($upgradeArr, $upgrade);
        }
        $serviceVersion = $lastServiceVersion;
        tpCache('system',['system_version'=>$serviceVersion['key_num']]);// 记录版本号
        Cache::clear();//清除数据缓存文件
        tpCache('global');
        // 删除下载的升级包
        $ziplist = glob($this->root_path.'*.zip');
        @array_map('unlink', $ziplist);
        // 推送回服务器  记录升级成功
        $this->UpdateLog($serviceVersion['key_num']);
        return ['code' => 1, 'msg' => "升级成功{$copy_data['msg']}"];
    }

    /**
     * @param type $fileUrl 下载文件地址
     * @param type $md5File 文件MD5 加密值 用于对比下载是否完整
     * @return string 错误或成功提示
     */         
    private function downloadFile($fileUrl,$md5File)
    {
        $downFileName = explode('/', $fileUrl); 
        $downFileName = end($downFileName);
        $saveDir = $this->root_path.$downFileName; // 保存目录
        tp_mkdir(dirname($saveDir));
        if(!file_get_contents($fileUrl)){
            return ['code' => 0, 'msg' => '官方升级包不存在']; // 文件存在直接退出
        }
        $ch = curl_init($fileUrl);            
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
        $file = curl_exec ($ch);
        curl_close ($ch);                                                            
        $fp = fopen($saveDir,'w');
        fwrite($fp, $file);
        fclose($fp);
        if(!eyPreventShell($saveDir) || !file_exists($saveDir)){
            return ['code' => 0, 'msg' => '下载保存升级包失败，请检查所有目录的权限以及用户组不能为root'];
        }
        return ['code' => 1, 'msg' => '下载成功'];
    }
    
	// 升级记录 log 日志
    private function UpdateLog($to_key_num)
	{
		$mysqlinfo = Db::query("SELECT VERSION() as version");
        $mysql_version  = $mysqlinfo[0]['version'];
        $vaules = array(
            'domain'=> Request::host(), //用户域名                
            'key_num'=> $this->curent_version, // 用户版本号
            'to_key_num'=> $to_key_num, // 用户要升级的版本号                
            'add_time'=> time(), // 升级时间
            'ip'    => serverIP(), // 服务器ip
            'phpv'  => phpversion(),// php版本
            'mysql_version' => $mysql_version, // mysql版本
            'web_server'    => $_SERVER['SERVER_SOFTWARE'], // 系统
        );
		$pcfcs = http_build_query($vaules);
        $url = base64_decode($this->service_pcfcms)."update.php/index/updatelog.html?pcfcs=".http_build_query($vaules);
        file_get_contents($url); 	
    }
	
} 
?>